﻿namespace Resonator.Core.Services
{
	using System;
	using System.Collections;
	using System.Collections.Generic;
	using Castle.ActiveRecord;
	using Castle.ActiveRecord.Framework;
	using Domain.Services;
	using NHibernate;
	using NHibernate.Expressions;
	using NHibernate.Search;
	using NHibernate.Search.Backend;
	using NHibernate.Search.Engine;
	using Resonator.Domain.Models;
	using NSearch = NHibernate.Search.Search;

	public class Repository<T> : IRepository<T> where T : class
	{
		public virtual T[] FindAll(DetachedCriteria detachedCriteria, params Order[] orders)
		{
			return ActiveRecordMediator<T>.FindAll( detachedCriteria, orders ); 
		}

		public virtual T[] FindAll(params DetachedCriteria[] detachedCriteria)
		{
			List<T> results = new List<T>(); 

			ISessionFactoryHolder holder = ActiveRecordMediator<T>.GetSessionFactoryHolder();
			ISession session = holder.CreateSession(typeof(T));
			try
			{
				IMultiCriteria multiCriteria = session.CreateMultiCriteria();
				foreach( DetachedCriteria criteria in detachedCriteria )
				{
					multiCriteria.Add( criteria ); 
				}

				IList resultSets = multiCriteria.List();
				foreach(IList resultSet in resultSets )
				{
					foreach( object oResult in resultSet )
					{
						T result = oResult as T;
						if( result != null && !results.Contains(result) )
						{
							results.Add( result ); 
						}
					}
				}

				return results.ToArray();
			}
			catch (Exception ex)
			{
				holder.FailSession(session);
				throw new ResonatorException("Could not perform FindAll for " + typeof(T).Name, ex);
			}
			finally
			{
				holder.ReleaseSession(session);
			}
		}

		public virtual T Find(object id)
		{
			return ActiveRecordMediator<T>.FindByPrimaryKey( id ); 
		}

		public virtual IList<T> Search(string query)
		{
			ISessionFactoryHolder holder = ActiveRecordMediator<T>.GetSessionFactoryHolder();
			ISession session = holder.CreateSession( typeof( T ) );
			try
			{
				IFullTextSession fullTextSession = NSearch.CreateFullTextSession( session );
				return fullTextSession.CreateFullTextQuery<T>( "FullText", query ).List<T>();
			}
			catch( Exception ex )
			{
				holder.FailSession( session );
				throw new ResonatorException( "Could not perform Search for " + typeof( T ).Name, ex );
			}
			finally
			{
				holder.ReleaseSession( session );
			}
		}

		public virtual void Save(T instance)
		{
			ActiveRecordMediator<T>.Save( instance ); 
		}

		public virtual ISessionScope CreateSessionScope()
		{
			return new TransactionScope();
		}

		public virtual DetachedCriteria CreateDetachedCriteria()
		{
			return DetachedCriteria.For<T>();
		}

		public virtual void DeleteAll()
		{
			ActiveRecordMediator<T>.DeleteAll();
		}

		public virtual void PurgeAll()
		{
			ISessionFactoryHolder holder = ActiveRecordMediator<T>.GetSessionFactoryHolder();
			ISession session = holder.CreateSession(typeof(T));
			try
			{
				try
				{
					IFullTextSession fullTextSession = NSearch.CreateFullTextSession( session );
					fullTextSession.PurgeAll( typeof( T ) );
				}
				catch( Exception ex )
				{
					holder.FailSession( session );
					throw new ResonatorException("Could not perform PurgeAll for " + typeof(T).Name, ex);
				}
			}
			finally
			{
				holder.ReleaseSession( session );
			}
		}

		public virtual void Optimize()
		{
			List<LuceneWork> queue = new List<LuceneWork>();
			queue.Add(new OptimizeLuceneWork(typeof(T)));

			ISessionFactoryHolder holder = ActiveRecordMediator<T>.GetSessionFactoryHolder();
			ISessionFactory sessionFactory = holder.GetSessionFactory( typeof( T ) ); 
			SearchFactory searchFactory = SearchFactory.GetSearchFactory(sessionFactory);
			searchFactory.ExecuteQueueImmediate(queue);
		}
	}
}